#include "defaults.h"
#include "utils.h"
#include "curverenderer.h"

using namespace std;

CurveRenderer::CurveRenderer(Curve* primitive, size_t samplesPatch, Vec3n color, bool renderControlPolygon, bool renderIsoCurves) : PrimitiveRenderer(primitive), samplesPatch(samplesPatch) {
	this->renderIsoCurves = renderIsoCurves;
	this->renderControlPolygon = renderControlPolygon;
	this->color = color;
	
	size_t pu = primitive->GetDegree();
	const Knotvector& U = primitive->GetKnotvector();
	vector<nfloat> Unew = DetermineParameters(U, primitive->Size() - 1, pu, 0.0, 1.0, samplesPatch);
	numisocurves = Unew.size();

	typedef boost::multi_array<Vec3n, 1>::index index;
	iso = new boost::multi_array<Vec3n, 1>(boost::extents[numisocurves]);
	iso_normal = new boost::multi_array<Vec3n, 1>(boost::extents[numisocurves]);

	nfloat p[1];
	DerivativesDim1* SKL = new DerivativesDim1(1);
	for(size_t i = 0; i < Unew.size(); i++) {
		p[0] = Unew[i];
		primitive->Derivative(p, SKL);
		(*iso)[i] = SKL->D[0];
	}
	delete SKL;
	Init();
}

CurveRenderer::~CurveRenderer() {
	delete iso;
	delete iso_normal;
	glDeleteLists(list, 1);
}

void CurveRenderer::Init() {
	typedef boost::multi_array<Vec3n, 1>::index index;

	list = glGenLists(1);
	glNewList(list, GL_COMPILE);
	glColor3f(0.0f, 0.0f, 0.0f);
	glDisable(GL_LIGHTING);
	glPushAttrib(GL_LINE_BIT);
	glLineWidth(3.0);
	glColor3f(color[0], color[1], color[2]);
	glBegin(GL_LINE_STRIP);
		for(index i = 0; i < numisocurves; i++) {
			const Vec3n& P = (*iso)[i];
			glVertex3d(P[0], P[2], P[1]);
		}
	glEnd();
	glPopAttrib();

	Curve* cprimitive = static_cast<Curve*>(primitive);
	const Knotvector& U = cprimitive->GetKnotvector();
	size_t p = cprimitive->GetDegree();

	DerivativesDim1 SKL(2);
	nfloat param[1];
	nfloat s = 0.01;

	if(renderIsoCurves) {
		glColor3f(0.0f, 0.0f, 0.0f);
		glBegin(GL_LINES);
		for(size_t i = p; i < U.size() - p; i++) {
			param[0] = U[i];
			cprimitive->Derivative(param, &SKL);

			Vec3n tangent = SKL.D[1];
			Vec3n binormal = cross(SKL.D[1], SKL.D[2]);
			normalize(tangent);
			normalize(binormal);
			Vec3n normal = cross(tangent, binormal);

			Vec3n normal1 = SKL.D[0] + s * normal;
			Vec3n normal2 = SKL.D[0] - s * normal;
			Vec3n binormal1 = SKL.D[0] + s * binormal;
			Vec3n binormal2 = SKL.D[0] - s * binormal;
			Vec3n tangent1 = SKL.D[0] + s * tangent;
			Vec3n tangent2 = SKL.D[0] - s * tangent;

			glVertex3d(normal1[0], normal1[2], normal1[1]);
			glVertex3d(normal2[0], normal2[2], normal2[1]);
			glVertex3d(binormal1[0], binormal1[2], binormal1[1]);
			glVertex3d(binormal2[0], binormal2[2], binormal2[1]);
			glVertex3d(tangent1[0], tangent1[2], tangent1[1]);
			glVertex3d(tangent2[0], tangent2[2], tangent2[1]);
		}
		glEnd();
	}

	if(renderControlPolygon) {
		glColor3f(1.0f, 1.0f, 0.0f);
		glBegin(GL_LINE_STRIP);
			for(size_t i = 0; i < cprimitive->Size(); i++) {
				const Vec4n& P = (*cprimitive)(i);
				glVertex4d(P[0], P[2], P[1], P[3]);
			}
		glEnd();
	}
	glEndList();
}

void CurveRenderer::Render() const {
	glCallList(list);
}
